﻿namespace Journey.Caching
{
    using System;
    using System.Collections.Generic;

    /// <summary>
    /// 默认缓存管理器。
    /// </summary>
    public class DefaultCacheManager : ICacheManager
    {
        /// <summary>
        /// 静态字典缓存数据。
        /// </summary>
        private readonly IDictionary<string, CacheEntity> _cache = new Dictionary<string, CacheEntity>();

        /// <summary>
        /// 锁定对象。
        /// </summary>
        private static readonly object LockObject = new object();

        /// <summary>
        /// 获取缓存值。
        /// </summary>
        /// <typeparam name="TValue">值类型</typeparam>
        /// <param name="key">键对象</param>
        /// <returns>值对象</returns>
        public TValue Get<TValue>(string key) where TValue : class
        {
            CacheEntity cache = null;

            if (this._cache.ContainsKey(key))
                cache = this._cache[key];

            return cache != null && cache.ExpiredTime > DateTime.UtcNow ? (TValue)cache.Value : null;
        }

        /// <summary>
        /// 获取缓存值，如果不存在，则添加缓存。
        /// </summary>
        /// <typeparam name="TValue">值类型</typeparam>
        /// <param name="key">键对象</param>
        /// <param name="acquire">获得值对象的委托。</param>
        /// <returns>值对象。</returns>
        public TValue GetOrAdd<TValue>(string key, Func<TValue> acquire) where TValue : class
        {
            var result = this.Get<TValue>(key) ?? acquire();

            this.Set(key, result);

            return result;
        }

        /// <summary>
        /// 增加缓存值。
        /// </summary>
        /// <typeparam name="TValue">值类型</typeparam>
        /// <param name="key">键对象</param>
        /// <param name="value">值对象</param>
        public void Set<TValue>(string key, TValue value) where TValue : class
        {
            this.Set(key, value, DateTime.UtcNow.AddMinutes(20));
        }

        /// <summary>
        /// 增加缓存值。
        /// </summary>
        /// <typeparam name="TValue">值类型。</typeparam>
        /// <param name="key">键对象。</param>
        /// <param name="value">值对象。</param>
        /// <param name="expiredTime">过期时间(UTC)。</param>
        /// <exception cref="ArgumentNullException">key 不可以是空的。</exception>
        public void Set<TValue>(string key, TValue value, DateTime expiredTime) where TValue : class
        {
            if (string.IsNullOrWhiteSpace(key))
                throw new ArgumentNullException("key", "键对象不可以是空的。");

            var cache = new CacheEntity
            {
                Value = value,
                ExpiredTime = expiredTime
            };

            lock (LockObject)
            {
                _cache[key] = cache;
            }
        }

        /// <summary>
        /// 移除缓存值。
        /// </summary>
        /// <typeparam name="TValue">值类型</typeparam>
        /// <param name="key">键对象</param>
        /// <param name="result">值对象</param>
        public void Remove<TValue>(string key, out TValue result) where TValue : class
        {
            result = null;

            if (_cache.ContainsKey(key))
            {
                result = (TValue)_cache[key].Value;
                _cache.Remove(key);
            }
        }

        /// <summary>
        /// 清空缓存。
        /// </summary>
        public void Clear()
        {
            _cache.Clear();
        }

        /// <summary>
        /// 缓存实体。
        /// </summary>
        private class CacheEntity
        {
            /// <summary>
            /// 缓存值。
            /// </summary>
            public object Value { get; set; }

            /// <summary>
            /// 过期时间（UTC）
            /// </summary>
            public DateTime ExpiredTime { get; set; }
        }
    }
}